﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Reflection;
using Microsoft.Internal;

namespace System.ComponentModel.Composition.ReflectionModel
{
    // Instead of representing properties as an actual PropertyInfo, we need to 
    // represent them as two MethodInfo objects one for each accessor. This is so 
    // that cached attribute part can go from a metadata token -> XXXInfo without 
    // needing to walk all members of a particular type. Unfortunately, (probably 
    // because you never see one of them in an IL stream), Reflection does not allow 
    // you to go from a metadata token -> PropertyInfo like it does with types, 
    // fields, and methods.

    internal class ReflectionProperty : ReflectionWritableMember
    {
        private readonly MethodInfo _getMethod;
        private readonly MethodInfo _setMethod;

        public ReflectionProperty(PropertyInfo property)
            : this(property, property.GetGetMethod(true), property.GetSetMethod(true))
        {
        }

        public ReflectionProperty(PropertyInfo property, MethodInfo getMethod, MethodInfo setMethod)
            : base(property)
        {
            Assumes.IsTrue(getMethod != null || setMethod != null);

            _getMethod = getMethod;
            _setMethod = setMethod;
        }

        public override bool CanRead
        {
            get { return _getMethod != null; }
        }

        public override bool CanWrite
        {
            get { return _setMethod != null; }
        }

        public override string Name
        {
            get
            {
                MethodInfo method = _getMethod ?? _setMethod;

                string name = method.Name;

                Assumes.IsTrue(name.Length > 4);

                // Remove 'get_' or 'set_'
                return name.Substring(3);
            }
        }

        public override bool RequiresInstance
        {
            get
            {
                MethodInfo method = _getMethod ?? _setMethod;

                return !method.IsStatic;
            }
        }

        public override Type ReturnType
        {
            get
            {
                if (_getMethod != null)
                {
                    return _getMethod.ReturnType;
                }

                ParameterInfo[] parameters = _setMethod.GetParameters();

                Assumes.IsTrue(parameters.Length > 0);

                return parameters[parameters.Length - 1].ParameterType;
            }
        }

        public override object GetValue(object instance)
        {
            Assumes.NotNull(_getMethod);

            return _getMethod.Invoke(instance, (object[])null);
        }

        public override void SetValue(object instance, object value)
        {
            Assumes.NotNull(_setMethod);

            _setMethod.Invoke(instance, new object[] { value });
        }

    }
}
